package cn.da.shuai.cool.finance.service.impl;

import cn.da.shuai.cool.finance.model.CoolAccountsReceivable;
import cn.da.shuai.cool.finance.mapper.CoolAccountsReceivableMapper;
import cn.da.shuai.cool.finance.service.ICoolAccountsReceivableService;
import cn.da.shuai.cool.mybatis.service.impl.CoolServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class CoolAccountsReceivableServiceImpl extends CoolServiceImpl<CoolAccountsReceivableMapper, CoolAccountsReceivable> implements ICoolAccountsReceivableService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean recordReceivable(CoolAccountsReceivable receivable) {
        receivable.setRemainingAmount(receivable.getAmount());
        receivable.setPaidAmount(BigDecimal.ZERO);
        receivable.setStatus("PENDING");
        return save(receivable);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean recordPayment(String invoiceNo, BigDecimal amount, String paymentReference) {
        CoolAccountsReceivable receivable = getOne(new LambdaQueryWrapper<CoolAccountsReceivable>()
                .eq(CoolAccountsReceivable::getInvoiceNo, invoiceNo));

        if (receivable == null) {
            throw new RuntimeException("Invoice not found");
        }

        BigDecimal newPaidAmount = receivable.getPaidAmount().add(amount);
        BigDecimal newRemainingAmount = receivable.getAmount().subtract(newPaidAmount);

        receivable.setPaidAmount(newPaidAmount);
        receivable.setRemainingAmount(newRemainingAmount);

        if (newRemainingAmount.compareTo(BigDecimal.ZERO) == 0) {
            receivable.setStatus("PAID");
        } else {
            receivable.setStatus("PARTIALLY_PAID");
        }

        // TODO: Record the payment in a separate payments table

        return updateById(receivable);
    }

    @Override
    public List<CoolAccountsReceivable> getOverdueReceivables() {
        LocalDate today = LocalDate.now();
        return list(new LambdaQueryWrapper<CoolAccountsReceivable>()
                .lt(CoolAccountsReceivable::getDueDate, today)
                .ne(CoolAccountsReceivable::getStatus, "PAID"));
    }

    @Override
    public Map<String, Object> getAgingAnalysis() {
        List<CoolAccountsReceivable> receivables = list(new LambdaQueryWrapper<CoolAccountsReceivable>()
                .ne(CoolAccountsReceivable::getStatus, "PAID"));

        LocalDate today = LocalDate.now();
        Map<String, BigDecimal> agingBuckets = new HashMap<>();
        agingBuckets.put("0-30", BigDecimal.ZERO);
        agingBuckets.put("31-60", BigDecimal.ZERO);
        agingBuckets.put("61-90", BigDecimal.ZERO);
        agingBuckets.put("90+", BigDecimal.ZERO);

        for (CoolAccountsReceivable receivable : receivables) {
            long daysOverdue = receivable.getDueDate().until(today).getDays();
            BigDecimal remainingAmount = receivable.getRemainingAmount();

            if (daysOverdue <= 30) {
                agingBuckets.put("0-30", agingBuckets.get("0-30").add(remainingAmount));
            } else if (daysOverdue <= 60) {
                agingBuckets.put("31-60", agingBuckets.get("31-60").add(remainingAmount));
            } else if (daysOverdue <= 90) {
                agingBuckets.put("61-90", agingBuckets.get("61-90").add(remainingAmount));
            } else {
                agingBuckets.put("90+", agingBuckets.get("90+").add(remainingAmount));
            }
        }

        return new HashMap<>(agingBuckets);
    }

    @Override
    public BigDecimal getCustomerBalance(Long customerId) {
        List<CoolAccountsReceivable> customerReceivables = list(new LambdaQueryWrapper<CoolAccountsReceivable>()
                .eq(CoolAccountsReceivable::getCustomerId, customerId)
                .ne(CoolAccountsReceivable::getStatus, "PAID"));

        return customerReceivables.stream()
                .map(CoolAccountsReceivable::getRemainingAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    @Override
    public List<Map<String, Object>> getReceivablesTrend(LocalDate startDate, LocalDate endDate) {
        List<CoolAccountsReceivable> receivables = list(new LambdaQueryWrapper<CoolAccountsReceivable>()
                .between(CoolAccountsReceivable::getInvoiceDate, startDate, endDate));

        Map<LocalDate, BigDecimal> dailyTotals = receivables.stream()
                .collect(Collectors.groupingBy(
                        CoolAccountsReceivable::getInvoiceDate,
                        Collectors.reducing(BigDecimal.ZERO, CoolAccountsReceivable::getAmount, BigDecimal::add)
                ));

        return dailyTotals.entrySet().stream()
                .map(entry -> {
                    Map<String, Object> dailyTotal = new HashMap<>();
                    dailyTotal.put("date", entry.getKey());
                    dailyTotal.put("amount", entry.getValue());
                    return dailyTotal;
                })
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean writeOff(String invoiceNo, String reason) {
        CoolAccountsReceivable receivable = getOne(new LambdaQueryWrapper<CoolAccountsReceivable>()
                .eq(CoolAccountsReceivable::getInvoiceNo, invoiceNo));

        if (receivable == null) {
            throw new RuntimeException("Invoice not found");
        }

        receivable.setStatus("WRITTEN_OFF");
        receivable.setDescription(reason);

        // TODO: Record the write-off in the general ledger

        return updateById(receivable);
    }

    @Override
    public Map<String, Object> getCollectionEfficiencyReport() {
        List<CoolAccountsReceivable> allReceivables = list();

        BigDecimal totalInvoiced = allReceivables.stream()
                .map(CoolAccountsReceivable::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal totalCollected = allReceivables.stream()
                .map(CoolAccountsReceivable::getPaidAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal collectionRate = totalCollected.divide(totalInvoiced, 4, BigDecimal.ROUND_HALF_UP)
                .multiply(new BigDecimal("100"));

        Map<String, Object> report = new HashMap<>();
        report.put("totalInvoiced", totalInvoiced);
        report.put("totalCollected", totalCollected);
        report.put("collectionRate", collectionRate);

        return report;
    }
}